import type { TextInput } from '../../../../components/text_input'
import type { ImageBaseInfo } from '../../../../lib/utils/browser/file'
import type { ComponentStyleIdType } from '../../../../types/interfaces'
import type {
  CachedImageInfo,
  ImagePreviewBaseConfig,
  ImagePreviewConfig,
  ImagePreviewPlatformConfig,
  PreviewState,
} from './types'
import QuickLRU from 'quick-lru'
import { BaseModule } from '../../../../lib/abstract'
import { ClickOutsideHandler } from '../../../../lib/click_outside_handler'
import { PageHandler } from '../../../../lib/page_handler'
import { GiteePageType } from '../../../../lib/page_handler/platform/gitee/types'
import { ToolKit } from '../../../../lib/utils/toolkit'
import {
  ComponentStyleId,
  getCurrentURL,
  HOSTNAME,
  logger,
  SUPPORTED_PLATFORMS,
  TIME_UNITS,
} from '../../../../types/constants'
import { SYMBOLS } from '../../../../types/interfaces'
import { ConfigManager } from '../../../config_manager'
import { ImagePreviewPreloadService } from './service/preload'
import { ImagePreviewUIService } from './service/ui'

interface Config {
  enabled?: boolean
  maxPreloadCount: number // 最大预加载数量
  minPreviewSize: number // 最大预览尺寸(px)
  performance: {
    cacheTtl: number // 缓存时间（毫秒）
    debounceDelay: number // 防抖延迟（毫秒）
  }
}

export class ImagePreview extends BaseModule {
  readonly id: string = 'image-preview'
  readonly styleId: string = ComponentStyleId[this.name]
  readonly namespace: string = `.${this.id}`
  isInitialized: boolean = false
  private static instance: ImagePreview
  readonly uiService: ImagePreviewUIService = new ImagePreviewUIService(this)
  private readonly preloadService: ImagePreviewPreloadService = new ImagePreviewPreloadService(this)
  private readonly configManager: ConfigManager = ConfigManager.getInstance()
  private readonly pageHandler = PageHandler.getInstance()
  private clickOutsideHandler: ClickOutsideHandler | null = null
  private textInput: TextInput | null = null
  readonly supportedPlatforms: string[] = this.configManager.moduleSupportedPlatforms(this.name)
  readonly eventNames: Record<'click' | 'buttonClick', string> = {
    click: `click${this.namespace}`,
    buttonClick: `click${this.namespace}.button`,
  }

  // 预览状态管理
  previewStates: Map<string, PreviewState> = new Map()

  // LRU 缓存
  private readonly maxImageCache: number = 1000 // 最大缓存数量
  imageCache: QuickLRU<string, CachedImageInfo> = new QuickLRU({ maxSize: this.maxImageCache })

  get currentConfig(): ImagePreviewConfig | undefined {
    return this.getCurrentConfig()
  }

  config: Config = {
    enabled: true,
    maxPreloadCount: 10, // 最大预加载数量
    minPreviewSize: 100, // 最小预览尺寸
    performance: {
      cacheTtl: TIME_UNITS.COMMON.INTERVALS.FIVE_MINUTES,
      debounceDelay: 300, // 防抖延迟（毫秒）
    },
  }

  get name(): ComponentStyleIdType {
    return 'ImagePreview'
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): ImagePreview {
    if (!ImagePreview.instance) {
      ImagePreview.instance = new ImagePreview()
    }
    return ImagePreview.instance
  }

  handleCleanupEventListeners(): void {
    this.removeAllEventListeners()
    this.clickOutsideHandler?.destroy()
  }

  handleCleanupCreatedElements(): void {
    this.uiService.removePreview()
    this.uiService.removeAllPreviewButtons()
  }

  handleCleanupStyles(): void {
    this.unregisterStyle(this.styleId)
  }

  handleCleanupOthers(): void {
    this.previewStates.clear()
    this.uiService.destroy()
    this.preloadService.destroy()
    this.clearImageCache()
  }

  core(): void {
    this.loadConfig()

    if (!this.config.enabled)
      return

    this.applyPreview()

    // 注册样式
    this.registerStyle(this.styleId, this.uiService.getStyles())
  }

  onToggleCallback(_id: string, checked: boolean, first: boolean): void {
    super.onToggleCallback(_id, checked, first)
  }

  /**
   * 获取当前页面的配置
   */
  getCurrentConfig(): ImagePreviewConfig | undefined {
    const config: ImagePreviewPlatformConfig = {
      [SUPPORTED_PLATFORMS.GITEE]: {
        [GiteePageType.CodeView]: {
          selectors: [
            {
              selector: '.el-tree .el-tree-node__children .el-tree-node__content .custom-left-node > div',
              getImageUrl: ($element: JQuery<HTMLElement>) => {
                const baseUrl = ToolKit.browser.url.getUrlPathnamePrefix(getCurrentURL(), 4)
                const filePath = this.pageHandler.pages.gitee.getFullFilePath($element)
                const url = `${baseUrl}/${filePath}`

                return this.pageHandler.pages.gitee.convertUrlToRawUrl(url, 'tree')
              },
            },
            {
              selector: '#tree-slider .tree-list-item[data-type="file"] a.tree-folder-item',
              getImageUrl: ($element: JQuery<HTMLElement>) => {
                const href = $element.attr('href')
                const url = ToolKit.browser.url.getFullUrl(href)

                return this.pageHandler.pages.gitee.convertUrlToRawUrl(url, 'blob')
              },
            },
          ],
          listen: true,
        },
      },
    }

    const currentConfig = config[HOSTNAME]?.[PageHandler.getCurrentPageType()]

    if (!currentConfig) {
      logger.warn(`[${this.name}] 未找到当前页面类型的配置`)
    }

    return currentConfig
  }

  applyPreview(): void {
    if (!this.currentConfig)
      return

    this.removeAllEventListeners()
    this.uiService.removeAllPreviewButtons()

    this.clickOutsideHandler = new ClickOutsideHandler({
      onOutsideClick: () => this.uiService.removePreview(),
      excludeSelectors: [`.${this.uiService.className}`, `.${this.uiService.buttonClassName}`],
    })

    const { selectors, listen: globalListen = false } = this.currentConfig

    // 为每个选择器添加事件监听
    selectors.forEach((config) => {
      let elementCount = 0
      const { selector, listen } = config
      const currentListen = listen ?? globalListen

      const handler = () => {
        const $elements = $(selector)

        if (!$elements.length) {
          logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 未找到匹配图片元素`, selector)
          return
        }

        $elements.each((_index, element) => {
          const $element = $(element)
          this.setupPreviewForElement($element, config)
          elementCount++
        })

        logger.info(`[${this.name}] ${SYMBOLS.STATUS.SUCCESS} ${elementCount} 个元素已启用图片预览功能`)
      }

      if (!currentListen) {
        handler()
      }
      else {
        this.pollingHandler.api.awaitElement(selector, handler)
      }
    })

    // 在所有选择器处理完成后，统一调度预加载
    this.preloadService.schedulePreload()
  }

  /**
   * 为元素设置预览功能
   */
  private setupPreviewForElement($element: JQuery<HTMLElement>, config: ImagePreviewBaseConfig): void {
    const elementId = `image-preview-${ToolKit.string.generateUniqueId()}`

    // 检查是否为图片元素
    const imageUrl = config.getImageUrl($element)
    const imagePath = ToolKit.browser.url.getUrlPathnameSuffix(imageUrl || '', 5)
    const imageTitle = this.getImageTitle($element, config)
    const isImage = ToolKit.browser.file.isImageFile(imageTitle)

    if (!imageUrl || !isImage || this.isDuplicateImage(imageUrl))
      return

    // 创建预览按钮
    const previewButton = this.uiService.createPreviewButton(elementId)

    // 保存预览状态
    const previewState: PreviewState = {
      element: $element,
      imageUrl,
      title: imageTitle,
      path: imagePath,
      previewElement: null,
      previewButton,
    }

    this.previewStates.set(elementId, previewState)
    this.uiService.previewElements.push(previewState)

    // 添加预览按钮到元素中
    $element.addClass(this.uiService.targetClassName).append(previewButton)

    logger.debug(`[${this.name}] 添加预览按钮到元素: ${imageTitle}`)
  }

  /**
   * 跳转到指定索引
   */
  async jumpToIndex(targetIndex: number): Promise<void> {
    if (targetIndex < 0 || targetIndex >= this.uiService.previewElements.length)
      return

    this.uiService.currentPreviewIndex = targetIndex
    await this.updatePreviewToIndex(targetIndex)
  }

  /**
   * 添加预览事件监听
   */
  addPreviewEventListeners(previewElement: JQuery<HTMLElement>): void {
    // 阻止预览框内部的点击事件冒泡
    previewElement.on('click', (e) => {
      e.stopPropagation() // 重要：防止点击内部时触发外部点击关闭
    })

    // 使用事件委托，减少监听器数量
    previewElement
      .off(this.eventNames.click)
      .on(this.eventNames.click, async (e) => {
        const $target = $(e.target)

        // 下载功能
        if ($target.closest('.image-preview-download').length) {
          e.stopPropagation() // 阻止冒泡

          const currentState = this.uiService.previewElements[this.uiService.currentPreviewIndex]
          const success = await this.downloadSingleImage(currentState)
          this.uiService.updateDownloadState(currentState, success)
        }

        if ($target.closest('.image-preview-download-all').length) {
          e.stopPropagation() // 阻止冒泡

          await this.downloadAllImages()
        }

        // 关闭功能
        if ($target.closest('.image-preview-close').length) {
          e.stopPropagation() // 阻止冒泡
          this.uiService.removePreview()
        }

        // 上一张
        if ($target.closest('.image-preview-prev').length) {
          e.stopPropagation() // 阻止冒泡
          await this.navigateToPrev()
        }

        // 下一张
        if ($target.closest('.image-preview-next').length) {
          e.stopPropagation() // 阻止冒泡
          await this.navigateToNext()
        }
      })

    // 导航到指定索引
    this.textInput?.mount(previewElement)

    // 键盘导航
    $(document).on('keydown.imagePreview', async (e) => {
      if (this.uiService.currentPreviewElement && this.uiService.currentPreviewElement.is(':visible')) {
        if (e.key === 'ArrowLeft') {
          e.preventDefault()
          await this.navigateToPrev()
        }
        else if (e.key === 'ArrowRight') {
          e.preventDefault()
          await this.navigateToNext()
        }
        else if (e.key === 'Escape') {
          e.preventDefault()
          this.uiService.removePreview()
        }
      }
    })
  }

  /**
   * 导航到上一张
   */
  private async navigateToPrev(): Promise<void> {
    if (this.uiService.previewElements.length <= 1)
      return

    this.uiService.currentPreviewIndex = (this.uiService.currentPreviewIndex - 1 + this.uiService.previewElements.length) % this.uiService.previewElements.length
    await this.updatePreviewToIndex(this.uiService.currentPreviewIndex)
  }

  /**
   * 导航到下一张
   */
  private async navigateToNext(): Promise<void> {
    if (this.uiService.previewElements.length <= 1)
      return

    this.uiService.currentPreviewIndex = (this.uiService.currentPreviewIndex + 1) % this.uiService.previewElements.length
    await this.updatePreviewToIndex(this.uiService.currentPreviewIndex)
  }

  /**
   * 下载全部图片
   */
  private async downloadAllImages(): Promise<void> {
    if (this.uiService.previewElements.length === 0) {
      logger.warn(`[${this.name}] 没有可下载的图片`)
      return
    }

    logger.info(`[${this.name}] 开始下载 ${this.uiService.previewElements.length} 张图片`)

    let successCount = 0
    let failedCount = 0

    // 使用并发控制，避免同时下载太多
    const CONCURRENT_LIMIT = 3
    for (let i = 0; i < this.uiService.previewElements.length; i += CONCURRENT_LIMIT) {
      const batch = this.uiService.previewElements.slice(i, i + CONCURRENT_LIMIT)
      const results = await Promise.allSettled(
        batch.map(state => this.downloadSingleImage(state)),
      )

      results.forEach((result, index) => {
        const state = batch[index]
        if (result.status === 'fulfilled' && result.value) {
          this.uiService.updateDownloadState(state, true)
          successCount++
        }
        else {
          failedCount++
          logger.warn(`[${this.name}] 下载失败: ${state.title}`)
        }
      })

      // 显示进度
      this.uiService.showDownloadProgress(successCount + failedCount, this.uiService.previewElements.length)

      // 短暂延迟，避免请求过于频繁
      if (i + CONCURRENT_LIMIT < this.uiService.previewElements.length) {
        await ToolKit.async.delay(1000)
      }
    }

    this.uiService.hideDownloadProgress()
    logger.info(`[${this.name}] 全部下载完成: ${successCount} 成功, ${failedCount} 失败`)
  }

  /**
   * 带重试的单张图片下载
   */
  private async downloadSingleImage(state: PreviewState, maxRetries: number = 2): Promise<boolean> {
    if (!state)
      return false

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        ToolKit.browser.file.downloadFile(state.imageUrl, {
          filename: ToolKit.browser.file.getImageFileName(state.imageUrl),
        })
        return true
      }
      catch (error) {
        if (attempt === maxRetries) {
          logger.error(`[${this.name}] 下载失败（尝试 ${attempt} 次）: ${state.title}`, error)
          return false
        }
        await ToolKit.async.delay(1000 * attempt) // 指数退避
      }
    }
    return false
  }

  /**
   * 检查下载状态
   */
  checkDownloadStatus(imageUrl: string): boolean {
    const state = this.uiService.previewElements.find(s => s.imageUrl === imageUrl)
    return state?.downloaded || false
  }

  /**
   * 更新到指定索引的预览
   */
  private async updatePreviewToIndex(index: number): Promise<void> {
    const state = this.uiService.previewElements[index]
    if (!state)
      return

    // 获取图片信息（如果还没有获取过）
    if (!state.imageInfo)
      state.imageInfo = await ToolKit.browser.file.getImageInfo(state.imageUrl)

    // 更新预览内容
    this.uiService.updatePreviewContent(state)

    // 更新导航状态
    this.uiService.updateNavigationState()

    logger.info(`[${this.name}] 切换到图片: ${state.title}`)
  }

  /**
   * 移除所有事件监听器
   */
  private removeAllEventListeners(): void {
    $(`.${this.uiService.targetClassName}`).off(this.eventNames.click)
    $(`.${this.uiService.className}`).off(this.eventNames.click)
    $(`.${this.uiService.buttonClassName}`).off(this.eventNames.buttonClick)
    this.previewStates.clear()
  }

  /**
   * 检查图片是否重复
   */
  private isDuplicateImage(imageUrl: string): boolean {
    const isDuplicate = this.uiService.previewElements.some(state => state.imageUrl === imageUrl)

    if (isDuplicate) {
      logger.debug(`[${this.name}] 跳过重复图片: ${imageUrl}`)
    }

    return isDuplicate
  }

  /**
   * 获取图片标题
   */
  getImageTitle($element: JQuery<HTMLElement>, config: ImagePreviewBaseConfig): string {
    const { imageTitleFetchType, getImageTitle } = config

    if (getImageTitle)
      return getImageTitle($element)

    if (imageTitleFetchType === 'attr')
      return $element.attr('title') || ''

    return $element.text().trim() || ''
  }

  /**
   * 获取图片信息（带缓存）
   */
  async getImageMetadata(imageUrl: string): Promise<ImageBaseInfo> {
    const now = Date.now()
    // 检查缓存是否存在且未过期
    const cachedData = this.imageCache.get(imageUrl)
    const cacheAge = now - (cachedData?.timestamp || 0)

    if (cachedData) {
      if (cacheAge <= this.config.performance.cacheTtl) {
        // 更新最后访问时间
        cachedData.lastAccessed = now
        logger.debug(`[${this.name}] 使用缓存图片信息: ${imageUrl}`)
        return cachedData
      }
      else {
        // 缓存过期，删除
        this.imageCache.delete(imageUrl)
        logger.debug(`[${this.name}] 缓存过期，删除: ${imageUrl}`)
      }
    }

    // 获取新数据
    const fetchData = await ToolKit.browser.file.getImageInfo(imageUrl)

    if (fetchData && fetchData.size > 0) {
      // 更新缓存
      this.updateImageCache(imageUrl, fetchData)
    }

    return fetchData
  }

  /**
   * 更新图片缓存
   */
  updateImageCache(imageUrl: string, metadata: ImageBaseInfo): void {
    const cacheData: CachedImageInfo = {
      ...metadata,
      timestamp: Date.now(),
      lastAccessed: Date.now(),
    }

    this.imageCache.set(imageUrl, cacheData)
  }

  /**
   * 清空图片缓存
   */
  clearImageCache(): void {
    this.imageCache.clear()
    logger.info(`[${this.name}] 图片缓存已清空`)
  }

  applyConfig(config: Partial<Config>): void {
    super.updateConfig(config)

    this.applyPreview()

    logger.info(`[${this.name}] Config updated and apply`, this.config)
  }
}
